﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Xsl;

namespace DSSDataServiceGenerator.Transformation
{
    class CodeGenerator
        : XmlWriter
    {
        private CodeGenerator(string stylesheet, XElement document)
            : base()
        {
            this.StyleSheet = stylesheet;

            this.Document = document;

            this.Files = new List<Data.File>();

            this._StringBuilder = new StringBuilder();

            this._Writer = XmlWriter.Create(this._StringBuilder, this._Settings);
        }

        private StringBuilder _StringBuilder = null;

        private XmlWriter _Writer = null;

        private XmlWriterSettings _Settings = new XmlWriterSettings()
        {
            NamespaceHandling = NamespaceHandling.OmitDuplicates,
            NewLineHandling = NewLineHandling.Replace,
            ConformanceLevel = ConformanceLevel.Auto,
            Encoding = Encoding.UTF8,
            DoNotEscapeUriAttributes = false,
            NewLineOnAttributes = false,
            OmitXmlDeclaration = true,
            CheckCharacters = false,
            CloseOutput = true,
            Indent = true,
            NewLineChars = "\r\n",
            IndentChars = "\t"
        };

#if DEBUG
        static string DebugPath = @"..\..\";
#else
        static string DebugPath = @"";
#endif

        public static Data.File[] Generate(string stylesheet, XElement document)
        {
            return new CodeGenerator(stylesheet, document).Generate();
        }

        public string StyleSheet { get; private set; }

        public XElement Document { get; private set; }

        private Data.File[] Generate()
        {
            XslCompiledTransform transform = new XslCompiledTransform(true);

            transform.Load(DebugPath + this.StyleSheet, new XsltSettings(true, true), null);

            XsltArgumentList args = new XsltArgumentList();

            args.AddExtensionObject("http://sapura.com.my/codegenerator", this);

            transform.Transform(this.Document.CreateReader(), args, this);

            return this.Files.ToArray();
        }

        public List<Data.File> Files { get; private set; }

        public string NormalizeName(string name)
        {
            return string.Join("", name.Split('_').Select(item => item.First().ToString().ToUpper() + string.Join("", item.Skip(1))));
        }

        public void Save(string directory, string name)
        {
            this.Flush();

            string ex = System.IO.Path.GetExtension(name);
            string language = "Unkown";

            switch (ex)
            {
                case ".cs":
                    language = "Csharp";
                    break;
                case ".dbs":
                case ".xml":
                case ".html":
                case ".wsdl":
                    language = "Xml";
                    break;
                default:
                    break;
            }

            this.Files.Add(
                new Data.File()
                {
                    Directory = directory,
                    Name = name,
                    Content = this._StringBuilder.ToString(),
                    Path = string.Concat(@"\", directory, @"\", name),
                    Language = language,
                    Extension = ex,
                    IsSelectedToBePublished = true
                }
            );

            this._StringBuilder.Clear();

            this._Writer = XmlWriter.Create(this._StringBuilder, this._Settings);
        }

        public override void Flush()
        {
            this._Writer.Flush();
        }

        public override string LookupPrefix(string ns)
        {
            return this._Writer.LookupPrefix(ns);
        }

        public override void WriteBase64(byte[] buffer, int index, int count)
        {
            this._Writer.WriteBase64(buffer, index, count);
        }

        public override void WriteCData(string text)
        {
            this._Writer.WriteCData(text);
        }

        public override void WriteCharEntity(char ch)
        {
            this._Writer.WriteCharEntity(ch);
        }

        public override void WriteChars(char[] buffer, int index, int count)
        {
            this._Writer.WriteChars(buffer, index, count);
        }

        public override void WriteComment(string text)
        {
            this._Writer.WriteComment(text);
        }

        public override void WriteDocType(string name, string pubid, string sysid, string subset)
        {
            this._Writer.WriteDocType(name, pubid, sysid, subset);
        }

        public override void WriteEndAttribute()
        {
            this._Writer.WriteEndAttribute();
        }

        public override void WriteEndDocument()
        {
            this._Writer.WriteEndDocument();
        }

        public override void WriteEndElement()
        {
            this._Writer.WriteEndElement();
        }

        public override void WriteEntityRef(string name)
        {
            this._Writer.WriteEntityRef(name);
        }

        public override void WriteFullEndElement()
        {
            this._Writer.WriteFullEndElement();
        }

        public override void WriteProcessingInstruction(string name, string text)
        {
            this._Writer.WriteProcessingInstruction(name, text);
        }

        public override void WriteRaw(string data)
        {
            this._Writer.WriteRaw(data);
        }

        public override void WriteRaw(char[] buffer, int index, int count)
        {
            this._Writer.WriteRaw(buffer, index, count);
        }

        public override void WriteStartAttribute(string prefix, string localName, string ns)
        {
            this._Writer.WriteStartAttribute(prefix, localName, ns);
        }

        public override void WriteStartDocument(bool standalone)
        {
            this._Writer.WriteStartDocument(standalone);
        }

        public override void WriteStartDocument()
        {
            this._Writer.WriteStartDocument();
        }

        public override void WriteStartElement(string prefix, string localName, string ns)
        {
            this._Writer.WriteStartElement(prefix, localName, ns);
        }

        public override WriteState WriteState
        {
            get { return this._Writer.WriteState; }
        }

        public override void WriteString(string text)
        {
            this._Writer.WriteString(text);
        }

        public override void WriteSurrogateCharEntity(char lowChar, char highChar)
        {
            this._Writer.WriteSurrogateCharEntity(lowChar, highChar);
        }

        public override void WriteWhitespace(string ws)
        {
            this._Writer.WriteWhitespace(ws);
        }
    }
}
